<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Promise.all() 实现测试</title>
  <style>
    .success{
      color:green;
    }
    .error{
      color:red;
    }
    button{
      color:white;
      background-color: deepskyblue;
      border:none;
      border-radius: 5px;
      padding:8px 16px;
      cursor: pointer;
    }
    button:hover{
      background-color: #007bff;
    }
    .test-case{
      padding:15px;
      border:1px solid #ccc;
      border-radius: 5px;
      margin-bottom: 20px;
    }
    .res{
      margin-bottom: 10px;
      padding:10px;
      border-radius: 5px;
    }
  </style>
</head>
<body>
<h2>手写实现Promise.all() 测试</h2>

<div class="test-case">
  <h3>测试1：所有Promise都成功</h3>
  <button onclick="test1()">start</button>
  <div class="res" id="res1"></div>
</div>

<div class="test-case">
  <h3>测试2：有一个Promise失败</h3>
  <button onclick="test2()">start</button>
  <div class="res" id="res2"></div>
</div>

<div class="test-case">
  <h3>测试3：包含非Promise值</h3>
  <button onclick="test3()">start</button>
  <div class="res" id="res3"></div>
</div>

<div class="test-case">
  <h3>测试4：空数组测试</h3>
  <button onclick="test4()">start</button>
  <div class="res" id="res4"></div>
</div>

<div class="test-case">
  <h3>测试5：非数组输入测试</h3>
  <button onclick="test5()">start</button>
  <div class="res" id="res5"></div>
</div>

<script>
  /*
  设计思路：
  1.接收可迭代对象（通常是数组）
  2.返回一个新 Promise
  3.结果是数组，顺序与入参一致
  4.所有Promise完成后再resolve
  5.有一个失败就立即 reject
  * */

  //手写实现Promise.all()
  function myPromiseAll(promises) {
    return new Promise((resolve, reject) => {
      //检查是否可迭代（原生Promise.all要求参数是可迭代对象，通常是数组）
      if (typeof promises[Symbol.iterator] !== 'function') {
        return reject(new TypeError("object is not iterable (cannot read property Symbol(Symbol.iterator))"));
      }

      const results = [];
      let count = 0;

      //处理空数组情况
      if (promises.length === 0) {
        return resolve(results);
      }

      promises.forEach((promise, index) => {
        Promise.resolve(promise)
                .then(value => {
                  results[index] = value;
                  count++;

                  //当所有Promise都完成时resolve
                  if (count === promises.length) {
                    resolve(results);
                  }
                })
                .catch(reject);//任何一个Promise reject就直接reject
      });
    });
  }

  function handleTestRes(resElement,testInfo,myRes,anotherRes){
    resElement.innerHTML=`testing...`;

    Promise.all([
      myRes.catch(err=>({error: err})),
      anotherRes.catch(err=>({error: err}))
    ]).then(([myData,anotherData])=>{
      //比较错误类型和消息
      const isSame = (
              (myData.error && anotherData.error) &&
              (myData.error.constructor === anotherData.error.constructor) &&
              (myData.error.message === anotherData.error.message)
      ) || (
              !myData.error && !anotherData.error &&
              JSON.stringify(myData) === JSON.stringify(anotherData)
      );

      if(isSame){
        resElement.innerHTML=`
        <h4 class="success">测试结果一致(${testInfo})：</h4>
        <p>myPromiseAll结果：${myData.error ? myData.error.toString() : JSON.stringify(myData)}</p>
        <p>Promise.all()结果：${anotherData.error ? anotherData.error.toString() : JSON.stringify(anotherData)}</p>
        `
      }else{
        resElement.innerHTML=`
        <h4 class="error">测试结果不一致(${testInfo})：</h4>
        <p>myPromiseAll结果：${myData.error ? myData.error.toString() : JSON.stringify(myData)}</p>
        <p>Promise.all()结果：${anotherData.error ? anotherData.error.toString() : JSON.stringify(anotherData)}</p>
        `
      }
    });
  }

  //测试1：所有Promise都成功
  function test1(){
    const promises=[
      Promise.resolve(1),
      Promise.resolve(2),
      Promise.resolve(3)
    ];
    handleTestRes(
            document.getElementById('res1'),
            '所有Promise都成功',
            myPromiseAll(promises),
            Promise.all(promises)
    );
  }

  //测试2：有一个Promise失败
  function test2(){
    const promises=[
      Promise.resolve(6),
      Promise.reject('failed~'),
      Promise.resolve(1)
    ];
    handleTestRes(
            document.getElementById('res2'),
            '有一个Promise失败',
            myPromiseAll(promises),
            Promise.all(promises)
    );
  }

  //测试3：包含非Promise值
  function test3(){
    const promises=[
      Promise.resolve('hello'),
      666,//非Promise值会被Promise.resolve()包装
      Promise.resolve(88)
    ];
    handleTestRes(
            document.getElementById('res3'),
            '包含非Promise值',
            myPromiseAll(promises),
            Promise.all(promises)
    );
  }

  //测试4：空数组测试
  function test4(){
    const promises=[];
    handleTestRes(
            document.getElementById('res4'),
            '空数组测试',
            myPromiseAll(promises),
            Promise.all(promises)
    );
  }

  //测试5：非数组输入测试
  function test5(){
    const notArray={a:1};
    handleTestRes(
            document.getElementById('res5'),
            '非数组输入测试',
            myPromiseAll(notArray),
            Promise.all(notArray)
    );
  }
</script>
</body>
</html>